package priv.conceit.sc.hoxton.file.handle.source;

import cn.hutool.core.util.StrUtil;
import io.minio.MinioClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;
import priv.conceit.sc.hoxton.common.entity.file.dto.FilePathDTO;
import priv.conceit.sc.hoxton.common.entity.file.dto.FilePreviewDTO;
import priv.conceit.sc.hoxton.common.util.DateTimeUtil;
import priv.conceit.sc.hoxton.common.util.FileUtil;
import priv.conceit.sc.hoxton.common.util.IoUtil;
import priv.conceit.sc.hoxton.file.config.FileProperties;
import priv.conceit.sc.hoxton.file.handle.preview.AbsPreviewHandle;
import priv.conceit.sc.hoxton.file.handle.preview.PreviewHandleFactory;

import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import java.io.File;
import java.io.InputStream;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * sc-hoxton minio文件存储实现
 *
 * @author Conceit
 * 2020/5/12 增加方法
 * @since 1.0.0, 2020/5/12
 */
@Component
public  class MinioSourceHandle extends AbsSourceHandle {

    @Autowired
    FileProperties fileProperties;

    @Autowired(required = false)
    MinioClient minioClient;

    @Autowired
    PreviewHandleFactory previewHandleFactory;


    /**
     * 上传文件
     *
     * @param file 文件实体
     * @return FileDetail
     * @author conceit
     * @date 2020/5/15 21:21
     */
    @Override
    public FilePathDTO upload(File file) {
        return null;
    }

    /**
     * 上传文件
     *
     * @param file {@link org.springframework.web.multipart}
     * @return FilePathDTO
     * @author conceit
     * @date 2020/5/15 21:21
     */
    @Override
    public FilePathDTO upload(MultipartFile file) throws Exception{

        if(!FileUtil.hasUploadFile(file)){
            //todo exception
            throw new IllegalArgumentException();
        }

        LocalDateTime now=LocalDateTime.now();
        //获取中间文件夹名
        FilePathDTO filePathDetail=buildSavedMidPathName(now);
        //获取真实文件名
        buildSavedFileName(filePathDetail,file.getOriginalFilename());

        doUpload(file.getInputStream(),fileProperties.getSource().getMinio().getBucketName()
                    ,filePathDetail.getSavedMidPath()+filePathDetail.getSavedFileName(),file.getSize(),file.getContentType());
        return filePathDetail;
    }

    /**
     * 删除文件
     *
     * @param savedFileName  文件实际保存名
     * @return boolean
     * @author conceit
     * @date 2020/5/15 21:21
     */
    @Override
    public boolean del(String savedFileName) throws Exception{
        if(StrUtil.isBlank(savedFileName)){
            throw new IllegalArgumentException();
        }
        FilePathDTO filePathDetail=this.parseSavedFileName(savedFileName);

        minioClient.removeObject(fileProperties.getSource().getMinio().getBucketName(),filePathDetail.getSavedMidPath()+filePathDetail.getSavedFileName());
        return true;

    }

    /**
     * 预览文件
     *
     * @param savedFileName  文件实际保存名
     * @return FilePreviewDetail 封装的文件预览信息
     * @author conceit
     * @date 2020/5/15 21:21
     */
    @Override
    public FilePreviewDTO preview(String savedFileName) throws Exception{
        if(StrUtil.isBlank(savedFileName)){
            throw new IllegalArgumentException();
        }
        FilePathDTO filePathDetail=this.parseSavedFileName(savedFileName);

        //已转换的文件是否命中
        AbsPreviewHandle absPreviewHandle =previewHandleFactory.create(savedFileName);
        FilePreviewDTO convertTemp=absPreviewHandle.getConvertTemp(filePathDetail);
        if(Objects.nonNull(convertTemp)){
            return convertTemp;
        }

        //获取流
        InputStream inputStream=doGet(fileProperties.getSource().getMinio().getBucketName(),
                filePathDetail.getSavedMidPath()+filePathDetail.getSavedFileName());
        return  absPreviewHandle.preview(filePathDetail,inputStream);
    }

    /**
     * 下载文件
     *
     * @param savedFileName  文件实际保存名
     * @return byte[]
     * @author conceit
     * @date 2020/5/15 21:21
     */
    @Override
    public byte[] download(String savedFileName) throws Exception{
        if(StrUtil.isBlank(savedFileName)){
            throw new IllegalArgumentException();
        }
        FilePathDTO filePathDetail=this.parseSavedFileName(savedFileName);
        InputStream inputStream=doGet(fileProperties.getSource().getMinio().getBucketName(),
                    filePathDetail.getSavedMidPath()+filePathDetail.getSavedFileName());
        return IoUtil.toByteArray(inputStream);
    }

    /**
     * 生成 真实保存的文件名
     *
     * @param filePathDTO 封装的中间路径信息
     * @param originalName 文件原始名称
     * @return FilePathDetail
     * @author conceit
     * @date 2020/5/15 21:21
     */
    @Override
    protected FilePathDTO buildSavedFileName(@NotNull FilePathDTO filePathDTO,@NotBlank String originalName) {

        /*
         * 生成最终文件名 一级目录格式化名+二级目录格式化名+文件短编码+文件原始名
         */
        String shortCode = generateShortCode();
        StringBuilder savedFileName=new StringBuilder();
        savedFileName.append(filePathDTO.getFirstName());
        savedFileName.append(fileProperties.getUpload().getSavedName().getSignSeparator());
        savedFileName.append(filePathDTO.getSecondName());
        savedFileName.append(fileProperties.getUpload().getSavedName().getSignSeparator());
        savedFileName.append(shortCode);
        savedFileName.append(fileProperties.getUpload().getSavedName().getSignSeparator());
        savedFileName.append(originalName);
        filePathDTO.setOriginalName(originalName);
        filePathDTO.setShortCode(shortCode);
        filePathDTO.setSavedFileName(savedFileName.toString());

        /*
         * 生成全路径 前置文件夹+中间路径文件夹+最终文件生成名
         */
        StringBuilder fileFullPath = new StringBuilder();
        fileFullPath.append(filePathDTO.getPraeviaPath());
        fileFullPath.append(fileProperties.getUpload().getSavedName().getSignSeparator());
        fileFullPath.append(filePathDTO.getSavedMidPath());
        fileFullPath.append(fileProperties.getUpload().getSavedName().getSignSeparator());
        fileFullPath.append(savedFileName);
        filePathDTO.setFullPath(fileFullPath.toString());

        return filePathDTO;
    }

    /**
     * 生成 真实保存中间文件夹名 由一级和二级目录合并而成
     *
     * @param localDateTime 传入的时间
     * @return FilePathDTO 封装的路径信息
     * @author conceit
     * @date 2020/5/15 22:05
     */
    @Override
    protected FilePathDTO buildSavedMidPathName(LocalDateTime localDateTime) {
        FilePathDTO filePathDetail=new FilePathDTO();
        StringBuilder midPathName=new StringBuilder();
        //格式化一级目录
        String firstName= DateTimeUtil.format(localDateTime,fileProperties.getUpload().getSavedMidPath().getFirstFormat());
        midPathName.append(firstName);
        midPathName.append(fileProperties.getSource().getMinio().getPathSeparator());
        //格式化二级目录
        String secondName= DateTimeUtil.format(localDateTime,fileProperties.getUpload().getSavedMidPath().getSecondFormat());
        midPathName.append(secondName);
        midPathName.append(fileProperties.getSource().getMinio().getPathSeparator());

        filePathDetail.setSavedMidPath(midPathName.toString());
        filePathDetail.setFirstName(firstName);
        filePathDetail.setSecondName(secondName);
        filePathDetail.setPraeviaPath(fileProperties.getSource().getMinio().getBucketName());
        return filePathDetail;
    }


    /**
     * 解析已保存的文件名为相关文件路径等细节
     *
     * @param savedFileName 文件提交名称
     * @return FilePathDTO 封装的路径信息
     * @author conceit
     * @date 2020/5/15 21:21
     */
    @Override
    public FilePathDTO parseSavedFileName(String savedFileName) {
        if(StrUtil.isBlank(savedFileName)){
            throw new IllegalArgumentException();
        }
        FilePathDTO filePathDetail=new FilePathDTO();

        List<String> strs =
                Stream
                        .of(savedFileName.split(fileProperties.getUpload().getSavedName().getSignSeparator()))
                        .collect(Collectors.toList());
        String first=strs.get(0);
        String second=strs.get(1);
        filePathDetail.setFirstName(first);
        filePathDetail.setSecondName(second);

        String afterStr=strs.stream().skip(3).collect(Collectors.joining());

        filePathDetail.setOriginalName(afterStr);
        filePathDetail.setPraeviaPath(fileProperties.getSource().getMinio().getBucketName());

        StringBuffer midPath=new StringBuffer();
        midPath.append(first);
        midPath.append(fileProperties.getSource().getMinio().getPathSeparator());
        midPath.append(second);
        midPath.append(fileProperties.getSource().getMinio().getPathSeparator());
        filePathDetail.setSavedMidPath(midPath.toString());

        filePathDetail.setSavedFileName(savedFileName);

        StringBuffer fullPath=new StringBuffer();
        fullPath.append(fileProperties.getSource().getMinio().getBucketName());
        fullPath.append(fileProperties.getSource().getMinio().getPathSeparator());
        fullPath.append(midPath);
        fullPath.append(savedFileName);
        filePathDetail.setFullPath(fullPath.toString());

        return filePathDetail;
    }



    /**
     *
     * 执行上传接口
     *
     * @param inputStream 输入流
     * @param bucketName 桶名
     * @param midPathAndSavedFileName 中间路径和文件实际保存名
     * @param size 文件长度
     * @param contentType 媒体类型
     */
    private void doUpload(InputStream inputStream,String bucketName,
                          String midPathAndSavedFileName, Long size,String contentType) throws Exception{

        //上传文件
        boolean bucketExist = minioClient.bucketExists(fileProperties.getSource().getMinio().getBucketName());
        if (!bucketExist) {
            minioClient.makeBucket(fileProperties.getSource().getMinio().getBucketName());
        }
        minioClient.putObject(
                fileProperties.getSource().getMinio().getBucketName(),
                midPathAndSavedFileName,
                inputStream,
                size,
                null, null,
                contentType);
    }

    /**
     * doGet
     *
     * @param bucketName 桶名
     * @param midPathAndSavedFileName 中间路径和文件实际保存名
     * @return java.io.InputStream
     * @author conceit
     * @date 2020/5/22 11:35
     */
    private InputStream doGet(String bucketName,String midPathAndSavedFileName) throws Exception{
       return minioClient.getObject(bucketName,midPathAndSavedFileName);
    }




}
